package com.easy.frame.base.service.tree.pageTree;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.service.IService;
import com.easy.frame.base.basic.constant.DbConstants;
import com.easy.frame.base.basic.constant.NumberConstants;
import com.easy.frame.base.basic.exception.ServiceException;
import com.easy.frame.base.basic.msg.ExceptionMsg;
import com.easy.frame.base.basic.msg.TreeMsg;
import com.easy.frame.base.basic.pojo.BaseTreePO;
import com.easy.frame.base.basic.pojo.vo.BaseTreeVO;
import com.easy.frame.base.basic.util.AuthUtils;
import com.easy.frame.base.basic.util.ObjectUtils;
import com.easy.frame.base.service.tree.TreeHandler;
import com.easy.frame.base.service.tree.TreeUtil;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 树状图相关接口    备注：该接口只为了适应tp自身的业务代码，后期会跟TreeService合并
 * 1.mapper层的使用暂时只能使用plus里的固定方法
 * 这里使用lambda的mybatisPlus的方式会报错，提示是字符串拼接错误，
 * 所以更换成它的父级，而且使用字符串直接拼接，拼接的字符串在TreeMsg这个类统一管理，
 * 考虑到这么操作的原因是，该使用到的字段都是树结构的固定字段(name，nodeLeaf等都是固定的字段)；
 * 2.该工具类现有的功能有：(该类持续更新，现在还缺移动规则)
 * 1> 获取组织树结构getTree；
 * 2> 添加组织树节点add；
 * 3> 修改组织树节点(只是修改组织树的基础信息，并没有包含移动节点)
 * 4> 删除组织树节点(软删除的方式删除，修改deleted字段为1)
 * 5> 移动树节点(暂时跟TreeService功能一直，具体要看是移动规则)
 * <p>
 * 注：复杂的查询或者sql语法，就只能在自己的那个类中进行编写，不能被提取出来
 *
 * @author tp
 * @date 2021/2/25 13:56
 */
public interface PageTreeService<T extends BaseTreePO> extends IService<T> {

    /**
     * 查询树结构
     *
     * @param enabled 查询是否生效的(可选)
     * @param name    条件查询中的名称(可选)
     * @return BaseTreeVO 组织树结构
     */
    default BaseTreeVO getPageTree(int enabled, String name) {
        List<T> entityList = getBaseMapper().selectList(new QueryWrapper<T>()
                .eq(TreeMsg.TREE_ENABLED, enabled)
                .eq(TreeMsg.TREE_DELETED, DbConstants.DEFAULT_NO)
        );
        if (StringUtils.isEmpty(entityList)) {
            return null;
        }
        List<T> resultList;
        if (!StringUtils.isEmpty(name)) {
            List<T> collect = entityList.stream()
                    .filter(s -> s.getName().contains(name))
                    .collect(Collectors.toList());
            resultList = new ArrayList<>(collect);
            collect.forEach(s -> this.getParent(s, entityList, resultList));
        } else {
            resultList = entityList;
        }
        //set方式进行去重
        return TreeHandler.defaultInstance().getTree(new HashSet<>(resultList));
    }

    /**
     * 查询树结构（包含父级的模糊搜索查询方式）
     * 1.先查找所有，根据名称查找所有；
     * 2.查出包含名称的所有对象；
     * 3.获取节点的最小层级，通过层级获取一个或者多个同层级的对象；
     * 4.找出所有的下级和父级
     *
     * @param enabled 查询是否生效的(可选)
     * @param name    条件查询中的名称(可选)
     * @return BaseTreeVO 组织树结构
     */
    default BaseTreeVO getPageByTree(int enabled, String name) {
        List<T> entityList = getBaseMapper().selectList(new QueryWrapper<T>()
                .eq(TreeMsg.TREE_ENABLED, enabled)
                .eq(TreeMsg.TREE_DELETED, DbConstants.DEFAULT_NO)
        );
        if (StringUtils.isEmpty(entityList)) {
            return null;
        }
        //set方式进行去重
        return TreeHandler.defaultInstance().getTree(new HashSet<>(getBaseTreeVO(name, entityList)));
    }

    /**
     * 将上述getPageByTree方法抽离出来
     * 只对查出来的结果集进行过滤
     */
    default List<T> getBaseTreeVO(String name, List<T> entityList) {
        List<T> resultList = entityList;
        if (!StringUtils.isEmpty(name)) {
            List<T> collect = entityList.stream()
                    .filter(s -> s.getName().contains(name))
                    .collect(Collectors.toList());
            Integer minLevel;
            if (collect.size() == 1) {
                minLevel = collect.get(0).getLevel();
            } else if (collect.size() == 0) {
                return entityList;
            } else {
                minLevel = Collections.min(collect.stream().map(T::getLevel)
                        .collect(Collectors.toList()));
            }
            if (ObjectUtils.isNull(collect)) {
                return entityList;
            }
            List<T> minLevelEntity = collect.stream()
                    .filter(s -> s.getLevel().equals(minLevel)).collect(Collectors.toList());
            minLevelEntity.forEach(s -> {
                List<T> childrenList = this.getBaseMapper().selectList(new QueryWrapper<T>()
                        .likeRight(TreeMsg.TREE_PARENT_PATH, s.getPath()));
                collect.addAll(childrenList);
            });
            resultList = new ArrayList<>(collect);
            //补充上所有的父级
            List<T> finalResultList = resultList;
            minLevelEntity.forEach(s -> this.getParent(s, entityList, finalResultList));
        }
        return resultList;
    }

    /**
     * 添加树状对象
     *
     * @param entity 树状对象,这里直接使用entity对象接收前端数据，就不用DTO
     * @return T 对象返回值
     */
    @Transactional(rollbackFor = Exception.class)
    default T pageAdd(T entity) {
        //校验该name是否存在；
        checkName(entity);
        //设置本身路径，通过父路径可能含有多个，但是如果没有的话就说明这个第一个
        String maxPath = getBaseMapper().selectOne(new QueryWrapper<T>()
                .eq(TreeMsg.TREE_DELETED, entity.getDeleted())
                .eq(StrUtil.isNotBlank(entity.getParentPath()), TreeMsg.TREE_PARENT_PATH, entity.getParentPath())
                .isNull(StrUtil.isBlank(entity.getParentPath()), TreeMsg.TREE_PARENT_PATH)
                .orderByDesc(TreeMsg.TREE_PARENT_PATH)
                .last(TreeMsg.TREE_LIMIT_ONE)
                .select(TreeMsg.TREE_PATH)
        ).getPath();

        setEntity(entity, maxPath);
        getBaseMapper().insert(entity);

        //添加子分类之后，设置父节点的叶子节点为不是叶子节点
        if (!StringUtils.isEmpty(entity.getParentPath())) {
            T parentEntity = getBaseMapper().selectOne(new QueryWrapper<T>()
                    .eq(TreeMsg.TREE_PATH, entity.getParentPath()));
            parentEntity.setLeafNode(false);
            getBaseMapper().update(parentEntity, new UpdateWrapper<T>()
                    .eq(TreeMsg.TREE_PATH, parentEntity.getPath())
                    .eq(TreeMsg.TREE_DELETED, DbConstants.DEFAULT_NO)
                    .set(TreeMsg.TREE_LEAF_NODE, parentEntity.getLeafNode()));
        }
        return entity;
    }

    /**
     * 修改某一树状对象
     *
     * @param entity 树状对象,这里直接使用entity对象接收前端数据，就不用DTO
     * @return T 对象返回值
     */
    @Transactional(rollbackFor = Exception.class)
    default T pageModify(T entity) {
        //校验名称：同层级的不可以重复，不同层级的重复可以重复
        List<T> subOptList;
        if (!StringUtils.isEmpty(entity.getParentPath())) {
            subOptList = getBaseMapper().selectList(new QueryWrapper<T>()
                    .eq(TreeMsg.TREE_NAME, entity.getName())
                    .eq(TreeMsg.TREE_PARENT_PATH, entity.getParentPath())
                    .eq(TreeMsg.TREE_DELETED, DbConstants.DEFAULT_NO));
        } else {
            subOptList = getBaseMapper().selectList(new QueryWrapper<T>()
                    .eq(TreeMsg.TREE_NAME, entity.getName())
                    .eq(TreeMsg.TREE_LEVEL, entity.getLevel())
                    .eq(TreeMsg.TREE_DELETED, DbConstants.DEFAULT_NO));
        }
        boolean b = false;
        if (subOptList.size() >= 1) {
            b = subOptList.get(0).getId().equals(entity.getId());
        }
        if (subOptList.size() > 0 && !b) {
            throw new ServiceException(ExceptionMsg.ADD_ALREADY_EXISTS, entity.getName());
        }
        T oldSysMenusCustom = getBaseMapper().selectById(entity.getId());
        boolean changeToDisable = (oldSysMenusCustom.getEnabled() == 1) && (entity.getEnabled() == 0);
        List<T> sysMenusCustom = getBaseMapper().selectList(new QueryWrapper<T>()
                .eq(TreeMsg.TREE_PARENT_PATH, oldSysMenusCustom.getPath())
                .eq(TreeMsg.TREE_DELETED, DbConstants.DEFAULT_NO));
        boolean hasEnableChild = StringUtils.isEmpty(sysMenusCustom);
        if (!entity.getLeafNode() && changeToDisable && hasEnableChild) {
            throw new ServiceException("该分项下存在有效的子分项，不允许失效");
        }
        boolean changeToEnable = (oldSysMenusCustom.getEnabled() == 0) && (entity.getEnabled() == 1);
        T sysMenusCustomParent = getBaseMapper().selectOne(new QueryWrapper<T>()
                .eq(TreeMsg.TREE_PATH, oldSysMenusCustom.getPath())
                .eq(TreeMsg.TREE_ENABLED, DbConstants.DEFAULT_NO)
                .eq(TreeMsg.TREE_DELETED, DbConstants.DEFAULT_NO));
        //将失效的能设施分项改为有效时，需校验其所有父级设施分项是否有效，
        // 当存在失效的父级设施分项时，提示“存在失效的父级分项，请先将修改父级分项为有效”；
        if (changeToEnable && !StringUtils.isEmpty(entity.getParentPath())
                && StringUtils.isEmpty(sysMenusCustomParent)) {
            throw new ServiceException("存在失效的父级分项，请先将修改父级分项为有效");
        }
        entity.setUpdateBy(AuthUtils.getCurrentUserId());
        getBaseMapper().updateById(entity);
        return entity;
    }

    /**
     * 软删除的方式删除父节点(只是更新deleted的状态值为1：表示已删除)
     *
     * @param id 需要删除的id值
     */
    @Transactional(rollbackFor = Exception.class)
    default void pageUpdateDelete(Long id) {
        T entity = getBaseMapper().selectById(id);
        if (StringUtils.isEmpty(entity)) {
            throw new ServiceException(ExceptionMsg.ADD_ALREADY_EXISTS, id);
        }
        if (!entity.getLeafNode()) {
            throw new ServiceException("不是叶子节点，不允许直接删除，请先删除其子节点！");
        }
        entity.setDeleted(DbConstants.DEFAULT_YES);
        entity.setUpdateBy(AuthUtils.getCurrentUserId());
        getBaseMapper().updateById(entity);

        // 若删除的不是第一层级，则需更新其父节点为叶子节点
        if (!entity.getLevel().equals(NumberConstants.CONSTANTS_ONE)) {
            int count = getBaseMapper().selectCount(new QueryWrapper<T>()
                    .eq(TreeMsg.TREE_PARENT_PATH, entity.getParentPath())
                    .eq(TreeMsg.TREE_ENABLED, DbConstants.DEFAULT_YES)
                    .eq(TreeMsg.TREE_DELETED, DbConstants.DEFAULT_NO)
            );
            if (count == 0) {
                entity.setParentPath(entity.getParentPath());
                entity.setLeafNode(true);
                getBaseMapper().update(entity, new UpdateWrapper<T>()
                        .eq(TreeMsg.TREE_PATH, entity.getParentPath())
                        .eq(TreeMsg.TREE_DELETED, DbConstants.DEFAULT_NO)
                        .set(TreeMsg.TREE_LEAF_NODE, entity.getLeafNode()));
            }
        }
    }

    /**
     * 移动节点
     *
     * <p>
     * 1.更新原父节点（注意无原父节点情况）
     * 2.更新新父节点（注意无新父节点情况）
     * 3.遍历所有子节点，批量更新
     * 4.更新自己（前面的更新需要用到 entity 的 path 和 parentPath）
     * 此移动的方式：（移动的方式多种，暂时使用这种方式移动）
     * 1.移动叶节点到目标非叶子节点，在目的地的子节点中新增一个
     * 2.移动叶节点到目标叶子节点，将目的地的节点新增子节点，子节点中生成一个新的节点
     * 3.移动非叶节点同理
     *
     * @param movedNodeId     需要移动的节点id（可以是叶节点或非叶节点）
     * @param newParentNodeId 新父级节点id（可以是叶节点或非叶节点）
     */
    @Transactional(rollbackFor = Exception.class)
    default void moveNode(Integer movedNodeId, Integer newParentNodeId) {

        if (ObjectUtil.isEmpty(movedNodeId)) {
            throw new ServiceException("movedNodeId不能为空。");
        }

        // 记录需要修改的对象
        List<T> batchUpdateList = new ArrayList<>();

        T entity = getBaseMapper().selectById(movedNodeId);

        // 1.更新原父节点的叶子节点，没有就更新，有就不动（注意无原父节点情况，（以及节点）无父节点不能移动）
        this.checkAndModifyParentLeafNodeToYes(entity);

        String newParentPath;
        if (ObjectUtils.isNull(newParentNodeId)) {
            newParentPath = "";
        } else {
            //即将移动到的父级对象
            T parentNode = getBaseMapper().selectById(newParentNodeId);
            newParentPath = parentNode.getPath();

            // 2.更新新父节点
            if (parentNode.getLeafNode()) {
                parentNode.setLeafNode(false);
                getBaseMapper().updateById(parentNode);
            }
        }

        String oldChildPath = entity.getPath();
        String newChildPath = this.getNewChildPath(newParentPath);
        //以上到这一步是将移动的父节点更新，获取最新的子节点路径；

        // 3.遍历所有子节点，批量更新（获取父路径所有的子节点对象，批量设置父路径，层级和路径）（这里指的是移动带有子节点的节点对象）
        //查询移动的子节点是否拥有子节点对象集合
        List<T> childrenList = this.getBaseMapper().selectList(new QueryWrapper<T>()
                .likeRight(TreeMsg.TREE_PARENT_PATH, entity.getPath()));
        //要是有值则代表移动的是一个节点（包含叶子节点），没有值就代表是叶节点，移动方的不管，只更新目的地的路径
        if (CollectionUtil.isNotEmpty(childrenList)) {
            for (T s : childrenList) {
                s.setParentPath(s.getParentPath().replaceFirst(oldChildPath, newChildPath));
                s.setPath(s.getPath().replaceFirst(oldChildPath, newChildPath));
                s.setLevel(TreeUtil.getLevel(s.getPath()));
            }
            batchUpdateList.addAll(childrenList);
        }

        // 4.更新自己
        entity.setParentPath(newParentPath);
        entity.setPath(newChildPath);
        entity.setLevel(TreeUtil.getLevel(newChildPath));
        batchUpdateList.add(entity);

        this.updateBatchById(batchUpdateList);
    }


    /**
     * 获取 parentPath 下的新子路径
     *
     * @param parentPath 父节点路径
     * @return parentPath 下的最新子路径
     */
    default String getNewChildPath(String parentPath) {

        String newChildPath;
        String currentMaximumChildPath = this.getCurrentMaximumChildPath(parentPath);
        if (StrUtil.isEmpty(currentMaximumChildPath)) {
            newChildPath = TreeUtil.getFirstChildPath(parentPath);
        } else {
            newChildPath = TreeUtil.getNextChildPath(currentMaximumChildPath);
        }
        return newChildPath;
    }


    /**
     * 获取 parentPath下的 最大的子节点路径
     *
     * @param parentPath 指定父路径，可能和 entity.getParentPath 不一致
     * @return 当前最大的子节点路径
     */
    default String getCurrentMaximumChildPath(String parentPath) {
        T maxPathChild = getBaseMapper().selectOne(new QueryWrapper<T>()
                .eq(TreeMsg.TREE_PARENT_PATH, ObjectUtil.isEmpty(parentPath) ? null : parentPath)
                .eq(TreeMsg.TREE_DELETED, DbConstants.DEFAULT_NO)
                .orderByDesc(TreeMsg.TREE_PATH)
                .last("limit 1"));
        return ObjectUtil.isEmpty(maxPathChild) ? null : maxPathChild.getPath();
    }

    /**
     * 查询父节点
     *
     * @param entity 节点对象
     * @return 父节点
     */
    default T selectParent(T entity) {

        LambdaQueryWrapper<T> wrapper = new LambdaQueryWrapper<>();

        if (org.apache.commons.lang.StringUtils.isBlank(entity.getParentPath())) {
            throw new ServiceException("该节点为一级节点，不存在父节点！");
        }
        wrapper.eq(T::getPath, entity.getParentPath());

        List<T> list = this.getBaseMapper().selectList(new QueryWrapper<T>()
                .eq(TreeMsg.TREE_PATH, entity.getParentPath())
                .eq(TreeMsg.TREE_DELETED, DbConstants.DEFAULT_NO));

        if (CollectionUtil.isEmpty(list)) {
            throw new ServiceException("未找到父节点或该节点已经是最高节点，请联系管理员。");
        }

        if (list.size() > 1) {
            throw new ServiceException("查询到的父节点个数大于1，数据异常，请联系管理员。");
        }
        return list.get(0);
    }


    /**
     * 获取设备设施表树状结构 - 获取父级
     *
     * @param entity     对应的表对象 例如：OrgTenan0t
     * @param list       查询的所有的对象集合
     * @param resultList 父级对象集合
     */
    default void getParent(T entity, List<T> list, List<T> resultList) {
        String parentPath = entity.getParentPath();
        if (StringUtils.isEmpty(parentPath)) {
            return;
        }
        for (T s : list) {
            if (parentPath.equals(s.getPath())) {
                resultList.add(s);
                if (!StringUtils.isEmpty(parentPath)) {
                    getParent(s, list, resultList);
                }
            }
        }
    }

    /**
     * 前置任务，比如名字判重等工作
     * 有自己特定的业务场景，就实现这个方法，作为前置方法
     *
     * @param entity 树状对象
     */
    default void beforeTreeAddProcessor(T entity) {

    }

    /**
     * 后置任务
     * 有自己特定的业务场景，就实现这个方法，作为后置方法
     *
     * @param entity 树状对象
     */
    default void afterTreeAddProcessor(T entity) {

    }

    /**
     * 检查name
     *
     * @param s entity对象
     */
    default void checkName(T s) {
        T orgTenant = getBaseMapper().selectOne(new QueryWrapper<T>()
                .eq(TreeMsg.TREE_NAME, s.getName())
                .eq(TreeMsg.TREE_PARENT_PATH, s.getParentPath())
        );
        if (ObjectUtil.isNotEmpty(orgTenant)) {
            throw new ServiceException(ExceptionMsg.ADD_ALREADY_EXISTS, s.getName());
        }
    }

    /**
     * 检查name
     *
     * @param entity  entity对象
     * @param maxPath 最大路径
     */
    default void setEntity(T entity, String maxPath) {
        if (ObjectUtils.isNull(maxPath)) {
            entity.setPath(TreeUtil.getFirstChildPath(entity.getParentPath()));
        } else {
            entity.setPath(TreeUtil.getNextChildPath(maxPath));
        }
        entity.setName(entity.getName());
        entity.setLevel(TreeUtil.getLevel(entity.getPath()));
        entity.setCreateTime(new Date());
        entity.setLeafNode(true);
        entity.setCreateBy(AuthUtils.getCurrentUserId());
        entity.setUpdateBy(AuthUtils.getCurrentUserId());
    }

    /**
     * 判断是否修改父节点的是否叶子节点为 true
     * 并修改父节点是否为叶节点的状态
     *
     * @param entity 树状对象
     */
    default void checkAndModifyParentLeafNodeToYes(T entity) {
        if (StrUtil.isNotEmpty(entity.getParentPath())) {
            T parent = this.selectParent(entity);
            if (ObjectUtil.isNotEmpty(parent)) {
                Integer childCount = getBaseMapper().selectCount(new QueryWrapper<T>()
                        .eq(TreeMsg.TREE_PARENT_PATH, entity.getParentPath())
                        .eq(TreeMsg.TREE_ENABLED, DbConstants.DEFAULT_YES)
                        .eq(TreeMsg.TREE_DELETED, DbConstants.DEFAULT_NO)
                );
                parent.setLeafNode(childCount.equals(1));
                this.getBaseMapper().updateById(parent);
            }
        }
    }

}
